﻿using System;
using System.Reflection;
using System.Runtime.Serialization;
using Microsoft.WindowsAzure.StorageClient;

namespace InstanceController.StateMachineDAL
{
	/// <summary>
	/// This class models your data as it will represented in the azure storage. Azure reflects over this
	/// class to construct your row entity.  In the current version the TableServiceEntity is NOT marked
	/// as serializable.  Thus your classes will throw a runtime error if you try to serialize your
	/// table class objects.  This class also inherits from the ISerializable and includes methods to 
	/// provide serialization in your projects.
	/// </summary>
	[Serializable]
	public class StateDataModel : TableServiceEntity, ISerializable
	{
		#region Warnings - Comments - Solutions | Working with your data model and classes
		/*
		 * WARNING: It is highly recommended that you use the Class Diagram tool
		 * in VS 2010 to Add, Edit and Delete your class properties.
		 * 
		 * Using the tool has the benefit of cascading any changes to other projects in your solution 
		 * that reference this class and its properties.  For instance a web role cloud project that has
		 * a reference to this datamodel assembly and uses one or more of its properties will automatically
		 * cascade the changes as they occur in the Class Diagram tool.  Specifically suppose there were 
		 * a public property called "ProcessName" in this class.  Further suppose that a web page in a project 
		 * in the solution that has a reference to this class uses it in its code such as:
		  		protected void NextButton_Click(object sender, EventArgs e)
				{
					//Get the data fields from the screen
					_profileDataModel.ProcessName = TextboxAddress.Text;
				}
		 * If then the xxxTableClassDiagram.cd file is opened in VS 2010 you can view the "Class Details"
		 * window which is like the "Properties" window of a web control. Suppose now that you edit the
		 * "ProcessName" property to be "Address1". The result of that change in the datamodel changes the 
		 * property name here in this file (so you don't have to manually maintain your datamodel class). 
		 * Better still it reaches out to other projects in the solution that reference it and makes the 
		 * edit there as well. Thus after the change is made the web page code would be automatically 
		 * updated to:
		  		protected void NextButton_Click(object sender, EventArgs e)
				{
					//Get the data fields from the screen
					_profileDataModel.Address1 = TextboxAddress.Text;
				}
		 * 
		 * The Caveats - 
		 *	- changes may cause existing assemblies NOT in the solution to break.
		 *	- changes in data type most likely will break code.
		 *	- row/column data entities stored in Azure tables prior to the change will have a different 
		 *	  schema and will be orphaned.
		 * 
		 * Solutions to induced issues:
		 *  - For a solution ALWAYS do a Clean/Build or Rebuild solution (data type errors will be caught here.)
		 *  - Do normal testing.
		 *  - Where there is existing data, plan a data migration (recommended). Expedient fix is to ADD a new
		 *    property leaving the existing property (not recommended because: 1 - data bloat, 2 - maintenance is 
		 *    now manual again, 3 - subsequent programmers don't understand the datamodel and which field is the 
		 *    "true" field, 4 - Data induced errors particularly in old code versions.
		 */
		#endregion

		#region Constructors
		/// <summary>
		/// Constructor: Creates the class with the required PartitionKey and RowKey.
		/// </summary>
		/// <param name="partitionKey"></param>
		/// <param name="rowKey"></param>
		public StateDataModel(string partitionKey, string rowKey)
			: base(partitionKey, rowKey)
		{
			//Your custom initializations here.
		}

		/// <summary>
		/// The empty parameter constructor exists ONLY for results queries.
		/// </summary>
		public StateDataModel() { }
		#endregion

		#region Public Properties

		/// <summary>
		/// CounterName Property
		/// </summary>
		/// <remarks>Blocks all activity during startup.</remarks>
		public string CounterName { get; set; }

		/// <summary>
		/// ProcessName Property
		/// </summary>
		/// <remarks>When some scaling action starts this should be set to true to prevent other scaling actions from attempting to run.</remarks>
		public string ProcessName { get; set; }

		/// <summary>
		/// CurrentMetricValue Property
		/// </summary>
		public string CurrentMetricValue { get; set; }

		/// <summary>
		/// CommandTimeStamp Property
		/// </summary>
		public string CommandTimeStamp { get; set; }

		/// <summary>
		/// Tag Property
		/// </summary>
		public string Tag { get; set; }

		/// <summary>
		/// Action Property
		/// </summary>
		public string Action { get; set; }

		/// <summary>
		/// RoleName Property
		/// </summary>
		public string RoleName { get; set; }

		/// <summary>
		/// DeploymentSlot Property
		/// </summary>
		public string DeploymentSlot { get; set; }

		/// <summary>
		/// ActionResult Property
		/// </summary>
		public string ActionResult { get; set; }

		#endregion

		#region Serialization methods

		/// <summary>
		/// Deserializer/constructor - Initializes a new instance of the <see cref="StateDataModel"/> class. 
		/// This works even though the formal declaration specifies a consturctor with properties 
		/// because the .NET serialization is able to step around that issue.
		/// </summary>
		/// <param name="info">The info.</param>
		/// <param name="ctxt">The CTXT.</param>
		public StateDataModel(SerializationInfo info, StreamingContext ctxt)
		{
			//Loops through the info object setting all the properties that were passed to the method.
			foreach (var i in info)
			{
				var propertyInfo = GetType().GetProperty(i.Name);	//gets the property we are setting
				propertyInfo.SetValue(this, i.Value, null);				//sets the property in the "this" object to the value.
			}
		}

		/// <summary>
		/// Add all the properties that you want to serialize, usually all the properties.
		/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
		/// </summary>
		/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
		/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
		/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			//Gets all the properties for this class with flags that indicate public or in the instance.
			var propertyInfos = typeof(StateDataModel).GetProperties(BindingFlags.Public | BindingFlags.Instance);

			//Loop through all the properties adding their name and value pairs to the serialization info object.
			foreach (var propertyInfo in propertyInfos)
			{
				info.AddValue(propertyInfo.Name, propertyInfo.GetValue(this, null));
			}
		}

		#endregion

	}


}


